#!/usr/bin/env python

#
# Woxxy Listener / Sender
#
# Written by Fabio Rotondo
#
# Version 0.1
#

import sys, os

"""
from os3.net.CTPRPC3Server import CTPRPCServer
from os3.net.CTPRPC3Client import CTPRPCClient
"""
from os3.net.CTPRPCServer2 import CTPRPCServer
from os3.net.CTPRPCClient2 import CTPRPCClient, enable_encryption, enable_compression, destroy_client

from PyQt4 import QtCore
from PyQt4.QtCore import SIGNAL

import socket

import datetime

from const import *


class WoxxyServer ( object ):
	def __init__ ( self, instance = None ):
		self.instance = instance
		self._fh = {}

	def ask_for_file ( self, fname, size, secure ):
		print "ASK CLIENT: %s - size: %s - secure: %s" % ( fname, size, secure )
		res = self.instance.pippo ( fname, size, secure )
		print "***** RES: %s" % res
		return ( 1, 1 )

	def put ( self, fname, data, start = 0 ):
		if start: 
			dest_path = self._resolve_fname ( fname )
			fout = open ( dest_path, "wb" )
			self._fh [ fname ] = fout
		else:
			fout = self._fh [ fname ]

		if data:
			fout.write ( data )
		else:
			fout.close ()
			del self._fh [ fname ]
			self.instance.emit ( SIGNAL ( "file_received" ), fname )

		return "OK"


	def put_socket ( self, conn, addr ):
		# Leggo il nome del file
		fname = self._read_string ( conn )

		# Leggo la lunghezza del file
		file_len = self._read_string ( conn )

		print "--- FNAME: %s - %s" % ( fname, file_len )

		full_fname = self._resolve_fname ( fname )

		fout = open ( full_fname, "wb" )
		chunk = conn.recv ( CHUNK_SIZE )
		while len ( chunk ):
			fout.write ( chunk )
			chunk = conn.recv ( CHUNK_SIZE )

		print "--SEND FINE"

		fout.close ()
		self.instance.emit ( SIGNAL ( "file_received" ), fname )

	def _read_string ( self, conn ):
		# Leggo un byte (per la lunghezza del nome del file )
		_len = ord ( conn.recv ( 1 ) )

		# Leggo il nome del file
		return conn.recv ( _len )




	def _resolve_fname ( self, fname ):
		cfg = self.instance.options.get_cfg ()
		dest_path = os.path.join ( cfg.get ( "dest_path", "" ), fname )

		return dest_path

	def identify ( self ):
		cfg = self.instance.options.get_cfg ()
		return { "name" : cfg.get ( "name", "Unknown user" ) }

	def add_me ( self, ipaddr ):
		self.instance.emit ( SIGNAL ( "add_me" ), ipaddr )
		#self.instance.add_contact_from_ip ( ipaddr )



class WoxxyListener ( object ):
	def __init__ ( self, instance = None, port = WOXXY_PORT ):
		self.srv = CTPRPCServer ( ( '', port ) )
		#self.srv.register_class ( WoxxyServer, init_params_dict { "instance" : instance } )
		self.srv.register_class ( WoxxyServer, init_params_dict = { "instance" : instance } )

	def start ( self ):
		#self.srv.serve ()
		self.srv.serve_forever ()

	def quit ( self ):
		print "QUIT!"
		self.srv.run = False
		#self.srv._quit_children ()
		self.srv.destroy_connections ()


class WoxxyBroadcastListener ( object ):
	def __init__ ( self, instance = None, port = WOXXY_PORT ):
		self.host = ''
		self.port = port
		self.sock = None

	def start ( self ):
		self.sock = socket.socket ( socket.AF_INET, socket.SOCK_DGRAM )
		self.sock.setsockopt ( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
		self.sock.setsockopt ( socket.SOL_SOCKET, socket.SO_BROADCAST, 1 )
		self.sock.bind ( ( self.host, self.port ) )

	def quit ( self ):
		print "Broadcast QUIT!"


	def handle_request ( self ):
		message, address = self.sock.recvfrom ( self.port )
		print "Got data from: ", address, " - ", message
		return ( address, message )


class WoxxySend ( object ):
	def send ( self, dest_client, port, full_name, secure = False ):
		( is_ok, secure ) = self.ask_client ( dest_client, port, full_name, secure )

		if not is_ok:
			# TODO: messaggio "L'utente ha rifiutato il file"
			return

		self._send ( dest_client, port, full_name, secure )
		
		
	def _send ( self, dest_client, port, full_fname, secure = False ):
		if secure :
			self.put ( dest_client, port, full_fname )
		else:
			self.put_socket ( dest_client, port, full_fname )

	def _create_cli ( self, dest_client, port ):
		cli = CTPRPCClient ( ( dest_client, port ) )
		enable_encryption ( cli, False )
		enable_compression ( cli, False )

		return cli

	def ask_client ( self, dest_client, port, full_name, secure ):
		cli = self._create_cli ( dest_client, port )

		size = os.stat ( full_name ).st_size

		( is_ok, secure ) = cli.ask_for_file ( full_name, size, secure )
		destroy_client ( cli )

		return ( is_ok, secure )

		

	def put ( self, dest_client, port, full_fname ):
		print 1
		cli = self._create_cli ( dest_client, port )

		print 2
		fname = os.path.basename ( full_fname )

                if full_fname.find ( ":" ) != -1:
                        if full_fname [ 0 ] == '/':
                                full_fname = full_fname [ 1 : ]

                        full_fname = full_fname.replace ( "/", "\\" )


		is_first = True

		print 3
		print "START =============", datetime.datetime.now ()
		num_bytes = 0
		fin = open ( full_fname, "rb" )
		l = fin.read ( CHUNK_SIZE )
		while len ( l ) == CHUNK_SIZE:
			cli.put ( fname, l, start = is_first )
			is_first = False

			num_bytes += CHUNK_SIZE
			print num_bytes

			l = fin.read ( CHUNK_SIZE )


		if len ( l ): cli.put ( fname, l, start = is_first )

		cli.put ( fname, 0, False )

		fin.close ()

		print "END =============", datetime.datetime.now ()

		print "FINE"

		destroy_client ( cli )

		#cli.put ( fname, open ( full_fname, "rb" ) )

	def put_socket ( self, dest_client, port, full_fname ):
		print "--PUT SOCKET 1"
		sock = socket.socket ( socket.AF_INET, socket.SOCK_STREAM, 0 )
		sock.settimeout ( 10.0 )
		sock.connect ( ( dest_client, WOXXY_SOCKET ) )

		# Invio il nome del file
		self._send_string ( sock, os.path.basename ( full_fname ) )

		# Invio la lunghezza del file
		self._send_string ( sock, str ( int ( os.stat ( full_fname ).st_size ) ) )

		fin = open ( full_fname, "rb" )
		chunk = fin.read ( CHUNK_SIZE )

		curr_len = len ( chunk )
		while chunk:
			print "SEND: ", curr_len

			sock.sendall ( chunk )
			chunk = fin.read ( CHUNK_SIZE )
			curr_len += len ( chunk )

		print "SEND FINE"
		sock.shutdown ( socket.SHUT_RDWR )
		sock.close ()

	def _send_string ( self, sock, txt ):
		# Invio il nome del file in questo formato:
		# 1 byte == lunghezza nome del file
		# .... bytes = nome del file
		out = chr ( len ( txt ) )
		out += txt

		sock.sendall ( out )


	def scan ( self, port = WOXXY_PORT ):
		import os3.net.utils
		my_ipaddr = os3.net.utils.get_ipaddr ()
		base, me = my_ipaddr.rsplit ( ".", 1 )

		me = int ( me, 10 )

		t = 230
		while t < 256:
			dest_client = base + ".%d" % t
			print "Scan: ", dest_client,

			try:
				cli = CTPRPCClient ( ( dest_client, port ) )
				res = cli.identify ()
				print "OK: %s" % res
			except:
				print "NO"

			t += 1

	def identify ( self, ipaddr, port = WOXXY_PORT ):
		cli = CTPRPCClient ( ( ipaddr, port ) )
		res = cli.identify ()
		return res

	def add_me ( self, ipaddr_dest, my_ip_addr ):
		cli = CTPRPCClient ( ( ipaddr_dest, WOXXY_PORT ) )
		res = cli.add_me ( my_ip_addr )
		return "OK"

def WoxxyBroadcast ( msg = "add_me:2" ):
	#msg = socket.gethostbyname ( socket.gethostname () )
	dest = ( '<broadcast>', WOXXY_PORT )
	s = socket.socket ( socket.AF_INET, socket.SOCK_DGRAM )
	s.setsockopt ( socket.SOL_SOCKET, socket.SO_BROADCAST, 1 )
	s.sendto ( msg, dest )

		
		

if __name__ == '__main__':
	if len ( sys.argv ) > 1:
		if sys.argv [ 1 ] == 'scan':
			ws = WoxxySend ()
			ws.scan ()
		elif sys.argv [ 1 ] == 'send':
			ws = WoxxySend ()
			ws.put ( "10.0.20.136", WOXXY_PORT, sys.argv [ 2 ] )
		elif sys.argv [ 1 ] == 'serve':
			wl = WoxxyListener ()
			print "Server Started"
			wl.start ()
			
